using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using GraphConstructor;
using PhoneNetwork.AntColony;

namespace PhoneNetwork
{
    public class PhoneNetwork_Ant : IGraph
    {
        private bool _isWeighted = true;
        private bool _isDirected = true;
        private List<Edge> _edges = new List<Edge>();
        private List<Node> _nodes = new List<Node>();
        private List<Node> _startNodes = new List<Node>();
        internal static frmStatistics _frmStatistics;
        private List<PhoneCall> _phoneCalls;
        internal int completed;
        internal int totaltime;
        internal int failed;
        internal int TotalCallsMade;
        internal int MaxConcurrentCalls;
        internal int TotalCalls;

        public List<PhoneCall> PhoneCalls
        {
            get { return _phoneCalls; }
            set { _phoneCalls = value; }
        }

        public string Name
        { get { return "Phone Network"; } }
        public string Description
        { get { return "Phone Network"; } }
        public string Complexty
        { get { return "O ( )"; } }
        public bool IsWeighted
        {
            get { return _isWeighted; }
            set { _isWeighted = value; }
        }
        public bool IsDirected
        {
            get { return _isDirected; }
            set { _isDirected = value; }
        }
        public List<Edge> Edges
        {
            get { return _edges; }
            set { _edges = value; }
        }
        public List<Node> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }
        public List<Node> StartNodes
        {
            get { return _startNodes; }
            set { _startNodes = value; }
        }
        public PhoneNetwork_Ant()
        {
            completed = 0;
            totaltime = 0;
            failed = 0;
            TotalCallsMade = 0;
            MaxConcurrentCalls = 20;
            TotalCalls = 20;

            _phoneCalls = new List<PhoneCall>();
        }
        public List<Edge> MakePhoneCall(Node From, Node To, bool Starter)
        {
            //if (!Nodes.Contains(From) || !Nodes.Contains(To))
            //    return null;
            if ((From.Data as Phone).Visited)
                return null;
            if (From == To)
            {
                (To.Data as Phone).IsBusy = true;
                return new List<Edge>();
            }
            (From.Data as Phone).Visited = true;
            foreach (Edge e in From.Adj)
            {
                Wire wire = e.Data as Wire;
                if (wire.Capacity - wire.InUse <= 0)
                    continue;
                List<Edge> phoneCall = MakePhoneCall(e.NodeTo, To, false);
                if (phoneCall != null)
                {
                    phoneCall.Insert(0, e);
                    wire.InUse++;
                    (From.Data as Phone).Visited = false;
                    (From.Data as Phone).IsBusy = Starter;
                    return phoneCall;
                }
            }
            (From.Data as Phone).Visited = false;
            return null;
        }
        public bool TryMakePhoneCall(Node From, Node To)
        {
            if (!Nodes.Contains(From) || !Nodes.Contains(To))
                return false;
            if ((From.Data as Phone).Visited)
                return false;
            if (From == To)
            {
                //(To.Data as Phone).IsBusy = true;
                return true;// new List<Edge>();
            }
            (From.Data as Phone).Visited = true;
            foreach (Edge e in From.Adj)
            {
                Wire wire = e.Data as Wire;
                if (wire.Capacity - wire.InUse <= 0)
                    continue;
                bool phoneCall = TryMakePhoneCall(e.NodeTo, To);
                if (phoneCall)
                {
                    //phoneCall.Add(e);
                    wire.InUse++;
                    (From.Data as Phone).Visited = false;
                    //(From.Data as Phone).IsBusy = Starter;
                    return true;// phoneCall;
                }
            }
            (From.Data as Phone).Visited = false;
            return false;
        }
        public void EndPhoneCall(List<Edge> call, Phone Starter, Phone End)
        {
            Starter.IsBusy = false;
            End.IsBusy = false;
            foreach (Edge e in call)
            {
                (e.Data as Wire).InUse--;
                e.Weight = (e.Data as Wire).Capacity - (e.Data as Wire).InUse;

                (e.NodeFrom.Data as Phone).IsBusy = false;
                (e.NodeFrom.Data as Phone).Visited = false;
                (e.NodeTo.Data as Phone).IsBusy = false;
                (e.NodeTo.Data as Phone).Visited = false;
            }
        }

        internal class RandomCall : Random
        {
            List<Node> _phones;
            private Node _start;

            public Node Start
            {
                get { return _start; }
                set { _start = value; }
            }
            private Node _end;

            public Node End
            {
                get { return _end; }
                set { _end = value; }
            }

            public Node getNode(Node start, int len)
            {
                if (start.Adj.Count == 0)
                    if (len == 0)
                    {
                        if ((start.Data as Phone).IsBusy)
                            return null;
                        else
                            return start;
                    }
                    else
                        return null;
                if (len == 0)
                    if ((start.Data as Phone).IsBusy)
                        return null;
                    else
                        return start;
                int rand = base.Next() % start.Adj.Count;
                Node res;
                if (start.Adj[rand].Weight > 0 && (res = getNode(start.Adj[rand].NodeTo, len - 1)) != null)
                    if ((res.Data as Phone).IsBusy)
                        return null;
                    else
                        return res;
                foreach (Edge e in start.Adj)
                    if (e.Weight > 0)
                        if ((res = getNode(e.NodeTo, len - 1)) != null)
                            if ((res.Data as Phone).IsBusy)
                                return null;
                            else
                                return res;
                return null;
            }
            public override int Next()
            {
                int start = base.Next() % _phones.Count;
                int end = base.Next() % _phones.Count;
                Start = _phones[start];
                End = _phones[end];
                return end;
                int len = base.Next() % _phones.Count;
                Node res = null;
                if (!(_phones[start].Data as Phone).IsBusy)
                    res = getNode(_phones[start], len);
                if (res != null)
                {
                    _start = _phones[start];
                    _end = res;
                    return len;
                }
                else
                {
                    for (int i = len; i > 0; i--)
                        for (int j = 0; j < _phones.Count; j++)
                        {
                            if (!(_phones[j].Data as Phone).IsBusy)
                                if ((res = getNode(_phones[j], i)) != null)
                                {
                                    _start = _phones[j];
                                    _end = res;
                                    return i;
                                }
                        }
                }
                return -1;
            }
            public RandomCall(List<Node> Phones)
                : base(2)
            {
                _phones = Phones;
            }
        }
        internal class PhoneNetworkEnumerated : IEnumerator<IStep>
        {
            List<Edge> _edges = new List<Edge>();
            List<List<Edge>> _paths = new List<List<Edge>>();
            RandomCall _randomCall;
            Random _randomAction;
            PhoneNetwork_Ant g;
            Node n;
            IStep step;
            int i;

            public IStep Current
            {
                get { return step; }
            }

            public void Dispose()
            {

            }

            object System.Collections.IEnumerator.Current
            {
                get { return step; }
            }

            public bool MoveNext()
            {
                PhoneCall c;

                // if the simulation is about to exceed the total number of calls, stop the ticker
                if (g.TotalCallsMade >= g.TotalCalls)
                {
                    step.IsFinal = true;
                    return false;
                    // write the tables to disk
                    //Global.WriteTablesToDisk();
                    // enable the button again
                    //btnStart.Enabled = true;
                    // stop the ticker
                    //Ticker.Stop();
                }

                // create a new call
                if (g.MaxConcurrentCalls > g.PhoneCalls.Count)
                {
                    _randomCall.Next();

                    // add a new call with a randon source and destination node
                    g.PhoneCalls.Add(new PhoneCall(_randomCall.Start, _randomCall.End));
                    _frmStatistics.Add(EventType.StartCall, _randomCall.Start.ToString(), _randomCall.End.ToString(), "");
                }

                // One tick for each call
                for (int i = 0; i < g.PhoneCalls.Count; i++)
                {
                    // get the ith call
                    c = g.PhoneCalls[i];

                    // if the call duration is zero then the call has finished
                    if (c.Duration != 0 && !c.HasFailed)
                    {
                        // check if the ant is working its way backwards
                        if (c.AntDirection == AntDirection.Backward)
                        {
                            // retrace one step for the ant whist updating the table 
                            // on the way
                            if (c.NewValue == -1)
                            {
                                // 1. calculate new value
                                double total = c.Duration;
                                //dont use self for calc
                                double vnodes = c.VisitedNodes.Count - 1;
                                c.NewValue = ((1 / (vnodes / total)) / total) * 100;
                            }

                            if (c.VisitedNodes.Count > 0)
                            {
                                if (c.finalVisitedNodeCount == 0)
                                    c.finalVisitedNodeCount = c.VisitedNodes.Count;

                                // Pop top visited node from top
                                Node endNode = c.VisitedNodes[c.VisitedNodes.Count - 1];

                                c.FinalVisitedNodes.Add(endNode);

                                // remove it from the visited array
                                c.VisitedNodes.RemoveAt(c.VisitedNodes.Count - 1);

                                Node tNode;

                                Node LinkedConn = null;

                                if (c.VisitedNodes.Count > 0)
                                    LinkedConn = c.VisitedNodes[c.VisitedNodes.Count - 1];

                                // get pheromene table for each of visited
                                for (int j = c.VisitedNodes.Count - 1; j >= 0; j--)
                                {
                                    // get second, third, etc visited node
                                    tNode = c.VisitedNodes[j];

                                    // get the pheromone table for visitednode
                                    PheromoneTable p = (endNode.Data as Phone).PheromoneTables[tNode];

                                    if (p != null)
                                        p.UpdateProbabilities(c.NewValue, LinkedConn);

                                    LinkedConn = tNode;
                                }

                                if (c.VisitedNodes.Count == 0)
                                    c.Finished = true;
                            }
                        }

                        // if the node is not connected
                        if (c.AntDirection == AntDirection.Forward)
                        {
                            // request the next node
                            Edge e = c.ConnectNextNode();

                            //if no node is available then increment the wait time and continue
                            if (e == null)
                            {
                                c.waitTime++;
                            }// otherwise connect to the node
                            else if (Phone.Connect(e.NodeTo, c, c.DestinationNode))
                            {
                                Node node = e.NodeTo;
                                // set the current node id
                                c.CurrentNode = node;
                                step.State.Edges.Add(e);
                                step.State.Nodes.Add(node);
                                _frmStatistics.Edit(c.SourceNode.ToString(), c.DestinationNode.ToString(), _frmStatistics.GetPath(c.SourceNode.ToString(), c.DestinationNode.ToString()) + "->" + node.ToString());
                                // add the node as a visited node
                                c.VisitedNodes.Add(node);
                            }

                            // if the call has reached it destination
                            if (e != null && e.NodeTo == c.DestinationNode)
                            {
                                c.Successful = true;
                                // remove loops if option is selected
                                //if (chbLoopRemoval.Checked)
                                c.LoopRemoval();

                                // now Change the direction of the ant
                                c.AntDirection = AntDirection.Backward;
                            }
                        }

                        // Shorten duration by one tick
                        c.Duration--;
                    }

                    // if the node has finished or has timed out
                    if (c.Duration == 0 || c.Finished)
                    {
                        // was the call successful
                        if (c.Successful)
                        {
                            // increment the completed counter
                            g.completed++;
                            // add the node count to the total
                            g.totaltime += c.finalVisitedNodeCount;
                            // calculate the average and render the label

                            // TODO
                            //double ave = g.totaltime / g.completed;
                            //this.lblAverage.Text = ave.ToString();
                        }
                        else
                        {
                            //otherwise the calls failed
                            // update the failed call label
                            //FailedCalls.Text = ((int)++failed).ToString();
                            g.failed++;
                        }

                        // update the total calls label
                        //this.lblTotalCalls.Text = (++Global.TotalCallsMade).ToString();
                        g.TotalCallsMade++;

                        // teminate the calls and release network resourses
                        c.TerminateCall();

                        // add the call to the simulation output
                        //sim.Calls.Add(c);

                        // add the default blank label
                        //sim.AnnoText.Add("");

                        //int util = 0;
                        //// Add the total network utilisation as a total number of node connections
                        //for (int k = 0; k < Global.Nodes.Length; k++)
                        //    util += Global.Nodes[k].CallIDS.Count;

                        //sim.NetworkUtilisation.Add(util);

                        // nullify the call
                        g.PhoneCalls.Remove(c);
                        _frmStatistics.Add(EventType.EndCall, c.SourceNode.ToString(), c.DestinationNode.ToString(), "");

                        // The array has bee reduced so decrease the indexer
                        // so not to skip the next call in this loop
                        i--;
                    }
                }

                // refresh the drawing panel
                //if (!chbIO.Checked)
                //pnlGraph.Refresh();
                return true;
            }
            public void Reset()
            {
                step = new PhoneNetwork_AntStep();
                step.State = new PhoneNetwork_Ant();
                foreach (Edge e in g.Edges)
                    _edges.Add(e);
                foreach (Node n in g.Nodes)
                    (n.Data as Phone).GenerateDefaultPheromoneTables(g);
                _randomCall = new RandomCall(g.Nodes);
                _randomAction = new Random(Environment.TickCount);
                if (_paths != null)
                    foreach (List<Edge> path in _paths)
                        if (path.Count > 0)
                            g.EndPhoneCall(path, path[0].NodeFrom.Data as Phone, path[path.Count - 1].NodeTo.Data as Phone);
                step.State.Nodes.Clear();
                step.State.Edges.Clear();
            }

            public PhoneNetworkEnumerated(PhoneNetwork_Ant d)
            {
                g = d;
            }
        }
        internal class PhoneNetwork_AntStep : IStep
        {
            private bool _isFinal;
            private IGraph _state;

            public IGraph State
            {
                get { return _state; }
                set { _state = value; }
            }
            public bool IsFinal
            {
                get { return _isFinal; }
                set { _isFinal = value; }
            }
        }

        public IEnumerator<IStep> GetEnumerator()
        {
            PhoneNetworkEnumerated phoneNetwork = new PhoneNetworkEnumerated(this);
            foreach (Node n in Nodes)
                n.Data = new Phone(n.ID, n);
            foreach (Edge e in Edges)
                e.Data = new Wire(e.Weight);
            foreach (Node n in Nodes)
                for (int i = 0; i < n.Adj.Count; i++)
                {
                    Phone phone = n.Data as Phone;
                    //phone.Propability[i] = 100 / n.Adj.Count;
                }
            if (_frmStatistics != null)
                _frmStatistics.Dispose();
            _frmStatistics = new frmStatistics();
            _frmStatistics.Clear();
            _frmStatistics.Show();
            _frmStatistics.TopMost = true;
            //_frmStatistics.BringToFront();
            return phoneNetwork;
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
